#include "common.h"
#include "utils/systime.h"


#include "Vbasemul.h"

#define TEST_CNT 100000
#define SIM_MAX_CYCLES (TEST_CNT*100)
uint64_t total_cycles = 0;
Vbasemul *top;
void quit(int ret)
{
    delete top;
    exit(ret);
}
void dump_wave()
{
    return;
}

void simcycle(uint32_t n)
{
    total_cycles += n;
    while (n--)
    {
        top->clk = 0;
        top->eval();
        dump_wave();
        top->clk = 1;
        top->eval();
        dump_wave();
    }
    if (total_cycles > SIM_MAX_CYCLES)
        quit(-1);
}
void reset()
{
    top->clk = 0;
    top->rst = 1;
    top->flush = 0;
    top->eval();
    simcycle(10);
    top->rst = 0;
}
bool mul_test(uint64_t src1, uint64_t src2, uint8_t sign, uint8_t w)
{
    top->valid_i = 0;
    while (top->ready == 0)
        simcycle(1);
    total_cycles = 0;
    top->src1 = src1;
    top->src2 = src2;
    top->mul_signed = sign;
    top->mulw = w;
    top->valid_i = 1;

    uint8_t res_sign = (sign & 1 ? 1 : 0) ^ (sign & 3 ? 1 : 0);
    __int128_t res;
    __uint128_t x, y, resu;
    if (w)
    {
        if (sign & 1)
            x = (__uint128_t)abs((int64_t)(SEXT(src1, 32)));
        else
            x = (__uint128_t)((uint64_t)(SEXT(src1, 32)));
        if (sign & 3)
            y = (__uint128_t)abs((int64_t)(SEXT(src2, 32)));
        else
            y = (__uint128_t)((uint64_t)(SEXT(src2, 32)));
    }
    else
    {
        if (sign & 1)
            x = (__uint128_t)abs((int64_t)(SEXT(src1, 64)));
        else
            x = (__uint128_t)((uint64_t)(SEXT(src1, 64)));
        if (sign & 3)
            y = (__uint128_t)abs((int64_t)(SEXT(src2, 64)));
        else
            y = (__uint128_t)((uint64_t)(SEXT(src2, 64)));
    }
    uint64_t res_hi, res_lo;
    resu = x * y;
    if (res_sign)
    {
        res = -resu;
        res_hi = (uint64_t)(res >> 64);
        res_lo = (uint64_t)(res);
    }
    else
    {
        res_hi = (uint64_t)(resu >> 64);
        res_lo = (uint64_t)(resu);
    }
    if(w) 
    {
        res_hi = SEXT(res_hi,32);
        res_lo = SEXT(res_lo,32);
    }
    simcycle(1);
    top->valid_i = 0;
    while (top->valid_o == 0)
        simcycle(1);
    if (res_hi != top->res_hi || res_lo != top->res_lo)
    {
        printf("error occur: src1:%lu src2:%lu sign:0x%x w:%d\n", src1, src2, sign, w);
        printf("\tref: res: 0x%016lx 0x%016lx\n", res_hi, res_lo);
        printf("\tdut: res: ");
        if (res_hi != top->res_hi)
            printf(ANSI_FMT("0x%016lx ", ANSI_FG_RED), top->res_hi);
        else
            printf("0x%016lx ", res_hi);
        if (res_lo != top->res_lo)
            printf(ANSI_FMT("0x%016lx", ANSI_FG_RED), top->res_lo);
        else
            printf("0x%016lx", res_lo);
        printf("\n");
        return false;
    }
    return true;
}
const uint8_t MUL_SIGN[3] = {0, 2, 3};
int main(int argc, char **argv, char **env)
{
    top = new Vbasemul;
    srand(0);
    reset();
    printf("mul test start\n");
    uint64_t start_us,end_us;
    start_us = SYS_GetTicks();
    uint32_t test_cnt = 0;
    for (int i = 0; i < TEST_CNT; i++)
    {
        test_cnt++;
        int src[4];
        for (int j = 0; j < 4; j++)
        {
            src[j] = rand();
        }
        uint64_t src1 = *(uint64_t *)(&src[0]);
        uint64_t src2 = *(uint64_t *)(&src[2]);
        if (mul_test(src1, src2, MUL_SIGN[rand() % 3], rand() % 2) == false)
        {
            break;
        }
    }
    end_us = SYS_GetTicks();
    printf("mul test end\n");

    printf("test cnt:%u cost time : %.03lfms\n",test_cnt, (end_us - start_us) / 1000.0);
    printf("test speed is %d mul/ms\n", (int)(test_cnt / ((end_us - start_us) / 1000.0)));
    delete top;
    return 0;
}

#ifdef __cplusplus
extern "C"
{
#endif

    // DPI IMPORTS
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/Cache/axi_mem.v:1:33
    long long mem_read(long long raddr, svBit is_icache) { return 0; }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/Cache/axi_mem.v:2:30
    void mem_write(long long waddr, long long wdata, char wmask) { return; }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/Execute/Execute.v:198:32
    void other_inst(svBit ecall, svBit fence, svBit ebreak) { return; }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/sim/top.v:1:33
    long long pmem_read(long long raddr, char rsize, char is_icache) { return 0; }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/sim/top.v:2:30
    void pmem_write(long long waddr, long long wdata, char wmask) { return; }
    void raise_hard_intr() {}
    void submit_inst(long long pc, int inst, long long next_pc) {}
    void difftest_memory_access(long long addr) {}

#ifdef __cplusplus
}
#endif
